package com.kk.crowd.mvc.config;

import com.google.gson.Gson;
import com.kk.crowd.exception.AccessForbiddenException;
import com.kk.crowd.exception.LoginAcctAlreadyInUseException;
import com.kk.crowd.exception.LoginAcctAlreadyInUseForUpdateException;
import com.kk.crowd.exception.LoginFailedException;
import com.kk.crowd.util.CrowdUtil;
import com.kk.crowd.util.ResultEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

/**
 * 基于注解的异常处理类
 */
@ControllerAdvice
public class CrowdExceptionResolver {


    // 表示捕获到 NullPointerException 类型的异常对象由当前方法处理
    @ExceptionHandler(value = NullPointerException.class)
    public ModelAndView resolveNullPointerException(Exception exception, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "system-error";
        //调用通用异常处理方法
        return commonResolveException(exception, request, response, viewName);
    }

    // 表示捕获到 LoginFailedException 类型的异常对象由当前方法处理
    @ExceptionHandler(value = LoginFailedException.class)
    public ModelAndView resolveLoginFailedException(Exception exception, HttpServletRequest request, HttpServletResponse response) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "admin-login";
        //调用通用异常处理方法
        return commonResolveException(exception, request, response, viewName);
    }

    @ExceptionHandler(value = AccessForbiddenException.class)
    public ModelAndView resolveAccessForbiddenException(Exception exception,HttpServletRequest request,HttpServletResponse response) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "admin-login";
        //调用通用异常处理方法
        return commonResolveException(exception, request, response, viewName);
    }

    @ExceptionHandler(value = LoginAcctAlreadyInUseException.class)
    public ModelAndView resolveLoginAcctAlreadyInUseException(Exception exception,HttpServletRequest request,HttpServletResponse response) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "admin-add";
        // 调用通用异常处理方法
        return commonResolveException(exception, request, response, viewName);
    }

    @ExceptionHandler(value = LoginAcctAlreadyInUseForUpdateException.class)
    public ModelAndView resolveLoginAcctAlreadyInUseForUpdateException(Exception exception,HttpServletRequest request,HttpServletResponse response) throws IOException {
        // 只是指定当前异常对应的页面即可
        String viewName = "system-error";
        // 调用通用异常处理方法
        return commonResolveException(exception, request, response, viewName);
    }

    @ExceptionHandler(value = Exception.class)
    public ModelAndView resolveException(Exception exception, HttpServletRequest request, HttpServletResponse response) throws IOException {
        String viewName = "system-error";
        return commonResolveException(exception, request, response, viewName);
    }



    /**
     * 核心通用异常处理方法
     * @param exception SpringMVC捕获到的异常
     * @param request 为了判断当前请求是普通请求还是Ajax请求
     * @param response 为了能够将JSON字符串作为当前请求的响应数据返回给浏览器
     * @param viewName 指定要前往的视图名称
     * @return ModelAndView
     * @throws IOException
     */
    private ModelAndView commonResolveException(Exception exception, HttpServletRequest request, HttpServletResponse response,String viewName) throws IOException {

        //1、判断当前请求是普通请求还是Ajax请求，普通请求跳转页面，Ajax相应json
        boolean judgeResult = CrowdUtil.judgeRequestType(request);

        //2、如果是Ajax请求
        if (judgeResult){
            //3、从当前异常对象中获取异常信息
            String message = exception.getMessage();
            //4、创建ResultEntity
            ResultEntity<Object> resultEntity = ResultEntity.failed(message);
            //5、创建Gson对象
            Gson gson = new Gson();
            //6、将 resultEntity转化为JSON字符串
            String json = gson.toJson(resultEntity);
            //7、把当前 JSON 字符串作为当前请求的响应体数据返回给浏览器
                // ①获取 Writer 对象
            PrintWriter writer = response.getWriter();
                // ②写入数据
            writer.write(json);
            // 8.返回 null，不给 SpringMVC 提供ModelAndView对象，这样SpringMVC就知道不需要框架解析视图来提供响应，而是程序员自己提供了响应
            return null;
        }

        //如果不是Ajax请求

        // 9.创建 ModelAndView 对象
        ModelAndView modelAndView = new ModelAndView();
        // 10.将 Exception 对象存入模型
        modelAndView.addObject("exception",exception);
        // 11.设置目标视图名称
        modelAndView.setViewName(viewName);
        // 12.返回 ModelAndView 对象
        return modelAndView;
    }

}
